#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include "queue.h"
#include "task.h"
#define stack_size 32768

//int max_tasks;								//quantidade maxima de tarefas que podem ser executadas
int next_task_id;							//id da proxima tarefa
task_t * running_task_ptr;					//ponteiro para tarefa em execucao
task_t * ready_tasks;						//lista de tarefas prontas
task_t main_task;							//tarefa principal
//ucontext_t main_context, running_context;	//contexto de tarefas para main e contexto da tarefa em execucao


void task_init(){
	//inicializa a biblioteca de funcoes de gerenciamento de tarefas
	//inicializa a tarefa main_task
	getcontext(&(main_task.context));					//salva o contexto
	main_task.tid = 0;									//id da tarefa principal e 0
	main_task.context.uc_link = 0;						//finalizar programa ao terminar tarefa
	next_task_id = 1;									//nenhuma tarefa esta em execucao
	running_task_ptr = &main_task;						//nenhuma tarefa esta em execucao
	ready_tasks = NULL;									//nenhuma tarefa esta em execucao
	setvbuf (stdout, 0, _IONBF, 0);						//desativa o buffer da saida padrao (stdout), usado pela funcao printf
	#ifdef DEBUG										//se o debug estiver habilitado
	printf("task_init inicializou a biblioteca");		//mostrar na tela que a biblioteca de tarefas foi inicializada
	#endif
	}

int task_create (task_t * task_ptr, void (*start_routine)(void *),  void * arg){
	getcontext(&(task_ptr->context));					
	char * stack;										//ponteiro para a pilha
	stack = malloc (stack_size);						//aloca espaco de memoria para a pilha
	if (stack){											//se for possivel criar a pilha
		task_ptr->context.uc_stack.ss_sp = stack;		//salva o parametro stack pointer das variaveis de contexto
		task_ptr->stack_start = stack;					//salva ponteiro para inicio da pilha
		task_ptr->context.uc_stack.ss_size = stack_size;//salva o parametro de tamanho de pilha para este contexto, definido por stack_size
		task_ptr->context.uc_stack.ss_flags = 0;		//define as flags do contexto
		task_ptr->context.uc_link = 0;					//uc_link = 0 finaliza tarefa
	} 
	else{												//se nao for possivel criar a pilha
		perror("error creating stack: ");				//avisa ao usuario erro
		#ifdef DEBUG									//se o debug estiver habilitado
		printf("task_create falhou em executar");		//mostrar na tela que a funcao foi executada e criou a tarefa
		#endif
		return (-1);									//valor de retorno em caso de erro e -1
	}
	makecontext(&(task_ptr->context), (void (*) (void)) start_routine, 1, arg);
	task_ptr->tid = next_task_id;									//dar id para tarefa
	next_task_id++;													//ir para a proxima id livre
	task_ptr->next = NULL;											//nao esta numa fila
	task_ptr->prev = NULL;
	queue_append ((queue_t **) &ready_tasks, (queue_t *) task_ptr);	//adicionar tarefa para fila de tarefas
	#ifdef DEBUG													//se o debug estiver habilitado
	printf("task_create criou a tarefa %d", task_ptr->tid);			//mostrar na tela que a funcao foi executada e criou a tarefa
	#endif
	return 0;
}
int task_switch (task_t *task_ptr){
	/*alterna para o processamento de outra tarefa 
	task_ptr aponta para a tarefa a ser processada */
	task_t * old_task_ptr = running_task_ptr;							//a tarefa agora corrente sera suspensa
	running_task_ptr = task_ptr;										//a nova tarefa corrente e a tarefa apontada por task_ptr
	//if (task_ptr == (task_ptr->next)){								//se for o ultimo elemento (task_ptr = &(task_ptr->next) = &(task_ptr->prev))
	//	return swapcontext (&task_ptr->context,&(main_task.context));
	//}
	#ifdef DEBUG														//se o debug estiver habilitado
	printf("task_switch encerrou tarefa %d e iniciou tarefa %d",old_task_ptr->tid, task_ptr->tid);
																		//mostrar na tela que a funcao foi executada e trocou de tarefa em execucao
	#endif
	return swapcontext (&old_task_ptr->context, &task_ptr->context);	//retorna 0 se foi possivel fazer troca de contexto
}

void task_exit (int exit_code){
	/*finaliza uma tarefa
	retorna para a tarefa principal*/
	int id_finalizado = running_task_ptr->tid;
	queue_remove((queue_t **) &ready_tasks, (queue_t *) running_task_ptr);	//remove tarefa da fila de tarefas
//	free(running_task_ptr->stack_start); 									//libera espaco da pilha
	task_switch(&main_task);
	#ifdef DEBUG															//se o debug estiver habilitado
	printf("task_exit encerrou tarefa %d \n", id_finalizado);				//mostrar na tela que a funcao foi executada e finalizou a tarefa
	#endif
}

int task_id(){
	#ifdef DEBUG															//se o debug estiver habilitado
	printf("task_id retornou %d como tarefa ativa", running_task_ptr->tid);	//mostrar na tela qual é a tarefa ativa
	#endif
	return running_task_ptr->tid;
}
